home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / MacWT 0.04 / wt / render.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-31  |  34.9 KB  |  1,231 lines  |  [TEXT/MMCC]

  1. /*
  2. **  wt -- a 3d game engine
  3. **
  4. **  Copyright (C) 1994 by Chris Laurel
  5. **  email:  claurel@mr.net
  6. **  snail mail:  Chris Laurel, 5700 W Lake St #208,  St. Louis Park, MN  55416
  7. **
  8. **  This program is free software; you can redistribute it and/or modify
  9. **  it under the terms of the GNU General Public License as published by
  10. **  the Free Software Foundation; either version 2 of the License, or
  11. **  (at your option) any later version.
  12. **
  13. **  This program is distributed in the hope that it will be useful,
  14. **  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. **  GNU General Public License for more details.
  17. **
  18. **  You should have received a copy of the GNU General Public License
  19. **  along with this program; if not, write to the Free Software
  20. **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. */
  22.  
  23. /*#define PROFILE 1*/
  24.  
  25. #include <math.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #if PROFILE
  29. #include <sys/types.h>
  30. #include <unistd.h>
  31. #include <sys/time.h>
  32. #endif
  33. #include <stdio.h>
  34. #include "wt.h"
  35. #include "error.h"
  36. #include "fixed.h"
  37. #include "wtmem.h"
  38. #include "table.h"
  39. #include "view.h"
  40. #include "texture.h"
  41. #include "framebuf.h"
  42. #include "graphics.h"
  43. #include "world.h"
  44. #include "render.h"
  45.  
  46.  
  47. #define VIEW_WIDTH  (fb->fb_width)
  48. #define VIEW_HEIGHT (fb->fb_height)
  49.  
  50. /* These macros convert 16.16 fixed point numbers to and from a 2.30 format.
  51. **   The extra fractional precision is needed when doing doing 1 / distance
  52. **   calculations for perspective.
  53. */
  54. #define TO_FIX_2_30(f)   ((f) << 14)
  55. #define FROM_FIX_2_30(f) ((f) >> 14) 
  56. #define TO_FIX_8_24(f)   ((f) << 8)
  57. #define FROM_FIX_8_24(f) ((f) >> 8) 
  58. #define MAX_WALL_EVENTS 30
  59.  
  60. #define MAX_ERROR  SCREEN_WIDTH
  61.  
  62.  
  63. typedef struct {
  64.      Wall *wall;
  65.      fixed z, dz;
  66.      Boolean is_back_view;
  67. } Wall_start;
  68.  
  69. typedef Wall *Wall_end;
  70.  
  71. typedef struct {
  72.      int n_events;
  73.      Wall_start events[MAX_WALL_EVENTS];
  74. } Wall_start_list;
  75.  
  76. typedef struct {
  77.      int n_events;
  78.      Wall_end events[MAX_WALL_EVENTS];
  79. } Wall_end_list;
  80.  
  81. typedef struct {
  82.      Boolean is_back_view;
  83.      Wall *wall;
  84.      Boolean visible;
  85.      fixed pstart1, pend1, pstart2, pend2;
  86.      fixed dpstart1, dpend1, dpstart2, dpend2;
  87.      fixed z;
  88.      fixed dz;
  89. } Active_wall;
  90.  
  91. typedef struct {
  92.      fixed pstart1, pend1, pstart2, pend2;
  93.      Boolean is_back_view;
  94.      fixed top, bottom;
  95.      Region *front, *back;
  96.      Wall *wall;
  97.      fixed z;
  98. } Wall_intersection;
  99.  
  100. typedef struct {
  101.      fixed screen_dy, screen_dx;
  102.      fixed view_sin, view_cos;
  103.      fixed sin_dx, cos_dx;
  104.      fixed *sin_tab, *cos_tab, *row_view;
  105. } View_constants;
  106.  
  107.  
  108. static void transform_vertices(World *w, View *view);
  109. static void clip_walls(World *w, View *v);
  110. static void add_wall_events(View *v, Wall *wall,
  111.                 fixed x1, fixed px1, fixed x2, fixed px2);
  112. static void render_walls(World *w, View *v);
  113. static int add_events(Active_wall *active, int n_active, int column);
  114. static Boolean wall_obscured(Vertex *common, Vertex *v1, Vertex *v2);
  115. static int remove_events(Active_wall *active, int n_active, int column);
  116. static Wall_intersection *calc_wall_heights(Active_wall *active, int n_active,
  117.                         int column, fixed height,
  118.                         Wall_intersection *cur_slice);
  119. static void draw_walls(Wall_intersection *int_list, View *v);
  120. static void draw_floors(Wall_intersection *int_list, View *v);
  121. static void draw_floor_slices(Region *r, fixed start, fixed end, View *v,
  122.                   int column);
  123. static void draw_ceiling_slices(Region *r, fixed start, fixed end,
  124.                 View *v, int column);
  125. static fixed wall_ray_intersection(fixed Vx, fixed Vy, Wall *wall);
  126. static void init_buffers(void);
  127. static void calc_view_constants(View *v, int screen_width, int screen_height);
  128.  
  129.  
  130. static Framebuffer *fb = NULL;
  131. static Wall_start_list *start_events = NULL;
  132. static Wall_end_list *end_events = NULL;
  133. static Wall_intersection *intersections = NULL;
  134. static int *fb_rows = NULL;
  135. static View_constants view_constants;
  136.  
  137.  
  138. #if defined(__GNUC__) && defined(ARCH_i86)
  139. #include "slice-gas86.c"
  140. #else
  141. #include "slice.c"
  142. #endif
  143.  
  144.  
  145. void init_renderer(int fb_width, int fb_height)
  146. {
  147.      int i;
  148.  
  149.  
  150.      if (fb != NULL)
  151.       wtfree(fb);
  152.      if (start_events != NULL)
  153.       wtfree(start_events);
  154.      if (end_events != NULL)
  155.       wtfree(end_events);
  156.      if (fb_rows != NULL)
  157.       wtfree(fb_rows);
  158.  
  159.      fb = new_framebuffer(fb_width, fb_height);
  160.      start_events = wtmalloc(sizeof(Wall_start_list) * (fb_width + 1));
  161.      end_events = wtmalloc(sizeof(Wall_end_list) * (fb_width + 1));
  162.      fb_rows = wtmalloc(sizeof(int) * fb_height);
  163.      for (i = 0; i < fb_height; i++)
  164.       fb_rows[i] = i * fb_width;
  165. }
  166.  
  167.  
  168. #if PROFILE
  169. static double current_time(void)
  170. {
  171.      struct timeval tv;
  172.      struct timezone tz;
  173.  
  174.      tz.tz_minuteswest = 0;
  175.      tz.tz_dsttime = DST_NONE;
  176.      gettimeofday(&tv, &tz);
  177.  
  178.      return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
  179. }
  180. #endif
  181.  
  182. #ifdef ARCH_SUN
  183. static void *memmove(void *dest, void *src, size_t size)
  184. {
  185.      int *lsrc = (int *) src;
  186.      int *ldest = (int *) dest;
  187.  
  188.      size /= 4;
  189.      /* Here, we assume that size is a multiple of four.  That's the only
  190.      **   way in which memmove will be used.  This assumption is nasty,
  191.      **   but life is rough . . . I'm bitter about having to implement
  192.      **   memmove in the first place.
  193.      */
  194.      if (lsrc < ldest) {
  195.       ldest += size - 1;
  196.       lsrc += size - 1;
  197.       while (size-- > 0)
  198.            *ldest-- = *lsrc--;
  199.      } else {
  200.       while (size-- > 0)
  201.            *ldest++ = *lsrc++;
  202.      }
  203.  
  204.      return dest;
  205. }
  206. #endif
  207.  
  208.  
  209. void render(World *w, View *v)
  210. {
  211. #if PROFILE
  212.      static int fps_count = 0;
  213.      static double total_time = 0.0;
  214.      double start_time = current_time();
  215. #endif
  216.      init_buffers();
  217.      calc_view_constants(v, VIEW_WIDTH, VIEW_HEIGHT);
  218.      transform_vertices(w, v);
  219.      clip_walls(w, v);
  220.      clear_framebuffer(fb);
  221.      render_walls(w, v);
  222.      update_screen(fb);
  223.  
  224. #if PROFILE
  225.      fps_count++;
  226.      total_time += current_time() - start_time;
  227.      if (fps_count == 100) {
  228.       printf("fps = %3.2f\n", (double) fps_count / total_time);
  229.       fps_count = 0;
  230.       total_time = 0.0;
  231.      }
  232. #endif
  233. }
  234.  
  235.  
  236. static void transform_vertices(World *w, View *view)
  237. {
  238.      Vertex *vertex;
  239.      fixed view_sin, view_cos;
  240.      int i;
  241.  
  242.  
  243.      vertex = TABLE_ELEMENTS(w->vertices, Vertex);
  244.      view_sin = view_constants.view_sin;
  245.      view_cos = view_constants.view_cos;
  246.  
  247.      for (i = 0; i < TABLE_SIZE(w->vertices); i++, vertex++) {
  248.       fixed x = vertex->x - view->x;
  249.       fixed y = vertex->y - view->y;
  250.  
  251.       vertex->tx = fixmul(x, view_cos) - fixmul(y, view_sin);
  252.       vertex->ty = fixmul(x, view_sin) + fixmul(y, view_cos);
  253.       if (vertex->tx > view->eye_distance)
  254.            /* project point onto view plane */
  255.            vertex->proj = fixdiv(vertex->ty, vertex->tx);
  256.      }
  257. }
  258.  
  259.  
  260. static void clip_walls(World *w, View *v)
  261. {
  262.      Wall *wall = (Wall *) w->walls->table;
  263.      int i;
  264.  
  265.      for (i = 0; i < TABLE_SIZE(w->walls); i++, wall++) {
  266.       fixed x1, y1, px1, x2, y2, px2;
  267.       unsigned int outcode1, outcode2;
  268.  
  269.       x1 = wall->vertex1->tx;
  270.       x2 = wall->vertex2->tx;
  271.       /* See if the wall lies completely behid the view plane. */
  272.       if (x1 < v->eye_distance && x2 < v->eye_distance)
  273.            continue;
  274.  
  275.       y1 = wall->vertex1->ty;
  276.       px1 = wall->vertex1->proj;
  277.       y2 = wall->vertex2->ty;
  278.       px2 = wall->vertex2->proj;
  279.  
  280.       /*** Clipping ***/
  281.  
  282.          /* First, clip to the view plane (line, really, since we're
  283.       **   working in only two dimensions.)
  284.       */
  285.       if (x1 <= v->eye_distance) {
  286.            /* be careful for division overflow */
  287.            if (x2 - x1 < FIXED_EPSILON)
  288.             continue;
  289.            y1 = y1 + fixmul(v->eye_distance - x1,
  290.                 fixdiv(y2 - y1, x2 - x1));
  291.            px1 = y1;
  292.            x1 = v->eye_distance;
  293.       }
  294.       if (x2 <= v->eye_distance) {
  295.            if (x1 - x2 < FIXED_EPSILON)
  296.             continue;
  297.            y2 = y2 + fixmul(v->eye_distance - x2,
  298.                 fixdiv(y1 - y2, x1 - x2));
  299.            px2 = y2;
  300.            x2 = v->eye_distance;
  301.       }
  302.  
  303.       /* Now, clip to the sides of the view polygon. */
  304.       outcode1 = FIXED_SIGN(v->view_plane_size + px1);
  305.       outcode1 |= FIXED_SIGN(v->view_plane_size - px1) << 1;
  306.       outcode2 = FIXED_SIGN(v->view_plane_size + px2);
  307.       outcode2 |= FIXED_SIGN(v->view_plane_size - px2) << 1;
  308.  
  309.       /* trivial reject */
  310.       if ((outcode1 & outcode2) != 0)
  311.            continue;
  312.       /* check for trivial accept */
  313.       if ((outcode1 | outcode2) != 0) {
  314.            /* Damn . . . we need to clip. */
  315.            fixed slope, denom, y_diff;
  316.  
  317.            denom = (x2 - x1);
  318.            if (FIXED_ABS(denom) < FIXED_EPSILON) {
  319.             if (denom < 0)
  320.              slope = FIXED_MIN + v->view_plane_size;
  321.             else
  322.              slope = FIXED_MAX - v->view_plane_size;
  323.            } else
  324.             slope = fixdiv(y2 - y1, denom);
  325.  
  326.            if (outcode1 == 1) {
  327.             px1 = -v->view_plane_size;
  328.             y_diff = y1 - fixmul(x1, -v->view_plane_size);
  329.             slope += v->view_plane_size;
  330.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  331.              x1 -= fixdiv(y_diff, slope);
  332.             else
  333.              x1 = FIXED_MAX;
  334.            } else if (outcode1 == 2) {
  335.             px1 = v->view_plane_size;
  336.             y_diff = y1 - fixmul(x1, v->view_plane_size);
  337.             slope -= v->view_plane_size;
  338.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  339.              x1 -= fixdiv(y_diff, slope);
  340.             else
  341.              x1 = FIXED_MAX;
  342.            }
  343.  
  344.            if (outcode2 == 1) {
  345.             px2 = -v->view_plane_size;
  346.             y_diff = y2 - fixmul(x2, -v->view_plane_size);
  347.             slope += v->view_plane_size;
  348.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  349.              x2 -= fixdiv(y_diff, slope);
  350.             else
  351.              x2 = FIXED_MAX;
  352.            } else if (outcode2 == 2) {
  353.             px2 = v->view_plane_size;
  354.             y_diff = y2 - fixmul(x2, v->view_plane_size);
  355.             slope -= v->view_plane_size;
  356.             if (FIXED_ABS(slope) > FIXED_EPSILON)
  357.              x2 -= fixdiv(y_diff, slope);
  358.             else
  359.              x2 = FIXED_MAX;
  360.            }
  361.       }
  362.  
  363.       add_wall_events(v, wall, x1, px1, x2, px2);
  364.      }
  365. }
  366.  
  367.  
  368. /* Add a wall to the event list--one event is added for the start of the
  369. **   wall, and another is added to mark the end of the wall.
  370. */
  371. static void add_wall_events(View *v, Wall *wall,
  372.                 fixed x1, fixed px1, fixed x2, fixed px2)
  373. {
  374.      int fb1, fb2;
  375.      fixed z1, z2;
  376.      Wall_start *event;
  377.  
  378.  
  379.      /* convert to frame buffer coordinates */
  380.      px1 = fixdiv(px1, view_constants.screen_dx + 1);
  381.      px2 = fixdiv(px2, view_constants.screen_dx + 1);
  382.      fb1 = FIXED_TO_INT(px1) + (VIEW_WIDTH >> 1);
  383.      fb2 = FIXED_TO_INT(px2) + (VIEW_WIDTH >> 1);
  384.  
  385.      /* There's no need to deal with walls that start and end in the same
  386.      **   screen column.  In a properly contructed world, we're guaranteed
  387.      **   that throwing them away won't leave any gaps.
  388.      */
  389.      if (fb1 == fb2)
  390.       return;
  391.  
  392.      /* Here we use a 2.30 fixed point format.  The result of this calculation
  393.      **   is always between 1 and zero, as the distance can never be less
  394.      **   than the view plane distance.  The extra fractional bits are
  395.      **   critical for the inverses.  Note that using 2.30 restricts the
  396.      **   size of the view plane to something less than 2.
  397.      */
  398.      z1 = fixdiv(TO_FIX_2_30(v->eye_distance), x1);
  399.      z2 = fixdiv(TO_FIX_2_30(v->eye_distance), x2);
  400.  
  401.      if (fb1 < fb2) {
  402.  
  403.       event = &start_events[fb1].events[start_events[fb1].n_events];
  404.  
  405.       event->wall = wall;
  406.       event->z = z1;
  407.       event->dz = fixdiv(z2 - z1, INT_TO_FIXED(fb2 - fb1));
  408.       event->is_back_view = False;
  409.       start_events[fb1].n_events++;
  410.  
  411.       end_events[fb2].events[end_events[fb2].n_events] = wall;
  412.       end_events[fb2].n_events++;
  413.  
  414.      } else {
  415.  
  416.       event = &start_events[fb2].events[start_events[fb2].n_events];
  417.  
  418.       event->wall = wall;
  419.       event->z = z2;
  420.       event->dz = fixdiv(z1 - z2, INT_TO_FIXED(fb1 - fb2));
  421.       event->is_back_view = True;
  422.       start_events[fb2].n_events++;
  423.  
  424.       end_events[fb1].events[end_events[fb1].n_events] = wall;
  425.       end_events[fb1].n_events++;
  426.  
  427.      }
  428. }
  429.  
  430.  
  431. static void render_walls(World *w, View *v)
  432. {
  433.      static int last_wall_count = 0;
  434.      static Active_wall *active;
  435.      int column;
  436.      int active_count = 0;
  437.      fixed Vx, Vy, dVy;
  438.      Wall_intersection *cur_slice;
  439.  
  440.  
  441.      /* Make sure that the active list is large enough to hold all the
  442.      **   walls in a world.
  443.      */
  444.      if (last_wall_count != TABLE_SIZE(w->walls)) {
  445.       last_wall_count = TABLE_SIZE(w->walls);
  446.       if (active == NULL)
  447.            active = wtmalloc(sizeof(Active_wall) * last_wall_count);
  448.       else
  449.            active = wtrealloc(active, sizeof(int) * last_wall_count);
  450.  
  451.       /* I know this is really cheesy, but . . . */
  452.       if (intersections == NULL)
  453.            intersections = wtmalloc(sizeof(Wall_intersection) * 3000);
  454.       else
  455.            intersections = wtrealloc(intersections,
  456.                      sizeof(Wall_intersection) * 4000);
  457.      }
  458.  
  459.      /* Set up for fast calculation of view rays. */
  460.      Vx = v->eye_distance;
  461.      Vy = -v->view_plane_size;
  462.      dVy = fixdiv(fixmul(v->view_plane_size, INT_TO_FIXED(2)),
  463.                   INT_TO_FIXED(VIEW_WIDTH));
  464.  
  465.      /* Build a sorted wall intersection list for each screen column. */
  466.      cur_slice = intersections;
  467.  
  468.      for (column = 0; column < VIEW_WIDTH; column++) {
  469.       Active_wall *current, *last;
  470.       /* Keep track of distances of walls in the active list.  Notice that
  471.       **   we're not actually tracking the distances of walls, but 
  472.       **   1 / distance instead.  That's because we can linearly
  473.       **   interpolate 1 / distance.  Also, most calculations that
  474.       **   use distance are really using 1 / distance (i.e. distance
  475.       **   appears in the denominator.
  476.       */
  477.             
  478.       active_count = add_events(active, active_count, column);
  479.       cur_slice = calc_wall_heights(active, active_count, column,
  480.                     v->height, cur_slice);
  481.       active_count = remove_events(active, active_count, column);
  482.  
  483.       last = active + active_count - 1;
  484.       for (current = active; current <= last; current++) {
  485.            current->z += current->dz;
  486.            if (current->visible) {
  487.             current->pstart1 += current->dpstart1;
  488.             current->pend1 += current->dpend1;
  489.             current->pstart2 += current->dpstart2;
  490.             current->pend2 += current->dpend2;
  491.            }
  492.       }
  493.  
  494.       Vy += dVy;
  495.      }
  496.  
  497.      /* Once we have the per-column sorted intersection list, most of the
  498.      **   tricky stuff is done.  All that's left is to actually blast bytes
  499.      **   into the framebuffer using the routines in slice.c.
  500.      */
  501.      draw_walls(intersections, v);
  502.      draw_floors(intersections, v);
  503. }
  504.  
  505.  
  506. /* Add new walls to the active list.  The active list is kept
  507. **   depth sorted.  We have to be careful here.  Correct depth
  508. **   ordering of the walls is vital for rendering.  At corners
  509. **   we have two or more walls at the same distance; however,
  510. **   there is still a correct and incorrect ordering.  If one
  511. **   wall is obscured by another, the visible wall must be placed
  512. **   in front in the list.
  513. */
  514. static int add_events(Active_wall *active, int n_active, int column)
  515. {
  516.      int i, j;
  517.      Wall_start *event;
  518.      Wall *wall;
  519.      fixed z;
  520.  
  521.      for (i = 0; i < start_events[column].n_events; i++) {
  522.       event = start_events[column].events + i;
  523.  
  524.       wall = event->wall;
  525.       z = event->z;
  526.  
  527.       for (j = 0; j < n_active; j++) {
  528.            Wall *wall2 = active[j].wall;
  529.            Vertex *common, *v1, *v2;
  530.  
  531.            if (z < active[j].z - MAX_ERROR)
  532.             continue;
  533.            else if (z > active[j].z + MAX_ERROR)
  534.             break;
  535.  
  536.            /* See if the walls share a vertex. */
  537.            if (wall->vertex1 == wall2->vertex1) {
  538.             common = wall->vertex1;
  539.             v1 = wall->vertex2;
  540.             v2 = wall2->vertex2;
  541.            } else if (wall->vertex1 == wall2->vertex2) {
  542.             common = wall->vertex1;
  543.             v1 = wall->vertex2;
  544.             v2 = wall2->vertex1;
  545.            } else if (wall->vertex2 == wall2->vertex1) {
  546.             common = wall->vertex2;
  547.             v1 = wall->vertex1;
  548.             v2 = wall2->vertex2;
  549.            } else if (wall->vertex2 == wall2->vertex2) {
  550.             common = wall->vertex2;
  551.             v1 = wall->vertex1;
  552.             v2 = wall2->vertex1;
  553.            } else {
  554.             /* We have two walls which are really close
  555.             **   together, but share no vertices.  Because
  556.             **   of roundoff error, we don't know for certain
  557.             **   which one is really in front.  Ideally, this
  558.             **   situation will be avoided by creating worldfiles
  559.             **   which don't place non-adjoining walls extremely
  560.             **   close together.
  561.             */
  562.             if (z > active[j].z)
  563.              break;
  564.             else
  565.              continue;
  566.            }
  567.  
  568.            if (!wall_obscured(common, v1, v2) &&
  569.            wall_obscured(common, v2, v1))
  570.             break;
  571.       }
  572.  
  573.       /* Insert the wall into the active list. */
  574.       memmove(active + j + 1, active + j,
  575.           sizeof(Active_wall) * (n_active - j));
  576.       active[j].wall = wall;
  577.       active[j].z = z;
  578.       active[j].dz = event->dz;
  579.       active[j].visible = False;
  580.       active[j].is_back_view = event->is_back_view;
  581.       n_active++;
  582.      }
  583.  
  584.      return n_active;
  585. }
  586.  
  587.  
  588. /* Determine whether wall 1 is obscured by wall 2 from the view point.
  589. **   This will be the case if a halfplane defined by wall 1 contains both the
  590. **   view point and wall 2.  Wall 1 is defined by the points common and v1;
  591. **   wall2 is defined by command and v2.  Note that this function uses the
  592. **   transformed coordinates of the vertices, so the view point and view
  593. **   direction need not be passed explicitly.
  594. */
  595. static Boolean wall_obscured(Vertex *common, Vertex *v1, Vertex *v2)
  596. {
  597.      fixed x1, y1, x2, y2;
  598.      unsigned int sign1, sign2;
  599.  
  600.  
  601.      x1 = common->tx - v1->tx;
  602.      y1 = common->ty - v1->ty;
  603.      x2 = common->tx - v2->tx;
  604.      y2 = common->ty - v2->ty;
  605.  
  606.      /* There's some tricky stuff done here to try to find the signs of
  607.      **   cross products without actually doing any multiplication.  I'm
  608.      **   really not sure if avoiding a few multiplies is worth the extra
  609.      **   overhead of sign checking, but the profiler shows this function as
  610.      **   taking a surprisingly small percentage of execution time.
  611.      */
  612.      if (FIXED_PRODUCT_SIGN(x1, y2) ^ FIXED_PRODUCT_SIGN(x2, y1))
  613.       sign1 = FIXED_PRODUCT_SIGN(x1, y2);
  614.      else
  615.       sign1 = FIXED_SIGN(fixmul(x1, y2) - fixmul(x2, y1));
  616.      if (FIXED_PRODUCT_SIGN(x1, common->ty) ^
  617.      FIXED_PRODUCT_SIGN(common->tx, y1))
  618.       sign2 = FIXED_PRODUCT_SIGN(x1, common->ty);
  619.      else
  620.       sign2 = FIXED_SIGN(fixmul(x1, common->ty) - fixmul(common->tx, y1));
  621.      
  622.      if (sign1 ^ sign2)
  623.       return False;
  624.      else
  625.       return True;
  626. }
  627.  
  628.  
  629. /* Remove walls from the active list.  Return the number of remaining
  630. ** active walls.
  631. */
  632. static int remove_events(Active_wall *active, int n_active, int column)
  633. {
  634.      int i, j;
  635.      Wall *wall;
  636.  
  637.  
  638.      /* This is really inefficient, so I hope these event lists are small. */
  639.      for (i = 0; i < end_events[column].n_events; i++) {
  640.       wall = end_events[column].events[i];
  641.  
  642.       for (j = 0; j < n_active && active[j].wall != wall; j++) ;
  643.       n_active--;
  644.       memmove(active + j, active + j + 1,
  645.           sizeof(Active_wall) * (n_active - j));
  646.      }
  647.  
  648.      return n_active;
  649. }
  650.  
  651.  
  652. static Wall_intersection *calc_wall_heights(Active_wall *active, int n_active,
  653.                         int column, fixed height,
  654.                         Wall_intersection *cur_slice)
  655. {
  656.      fixed top = FIXED_ONE_HALF, bottom = -FIXED_ONE_HALF;
  657.  
  658.  
  659.      while (n_active-- > 0 && bottom < top) {
  660.       Wall *wall = active->wall;
  661.       fixed z = active->z;
  662.       fixed dz = active->dz;
  663.  
  664.       if (!active->visible) {
  665.            active->pstart1 =
  666.             fixmul2_30(TO_FIX_8_24(wall->front->floor - height), z);
  667.            active->pend1 =
  668.             fixmul2_30(TO_FIX_8_24(wall->back->floor - height), z);
  669.            active->pstart2 =
  670.             fixmul2_30(TO_FIX_8_24(wall->back->ceiling - height), z);
  671.            active->pend2 =
  672.             fixmul2_30(TO_FIX_8_24(wall->front->ceiling - height), z);
  673.            active->dpstart1 =
  674.             fixmul2_30(TO_FIX_8_24(wall->front->floor - height), dz);
  675.            active->dpend1 =
  676.             fixmul2_30(TO_FIX_8_24(wall->back->floor - height), dz);
  677.            active->dpstart2 =
  678.             fixmul2_30(TO_FIX_8_24(wall->back->ceiling - height), dz);
  679.            active->dpend2 =
  680.             fixmul2_30(TO_FIX_8_24(wall->front->ceiling - height), dz);
  681.            active->visible = True;
  682.       }
  683.       cur_slice->pstart1 = FROM_FIX_8_24(active->pstart1);
  684.       cur_slice->pend1 = FROM_FIX_8_24(active->pend1);
  685.       cur_slice->pstart2 = FROM_FIX_8_24(active->pstart2);
  686.       cur_slice->pend2 = FROM_FIX_8_24(active->pend2);
  687.  
  688.       if (active->is_back_view) {
  689.            cur_slice->front = wall->back;
  690.            cur_slice->back = wall->front;
  691.            cur_slice->pstart1 = cur_slice->pend1;
  692.            cur_slice->pend2 = cur_slice->pstart2;
  693.       } else {
  694.            cur_slice->front = wall->front;
  695.            cur_slice->back = wall->back;
  696.       }
  697.  
  698.       cur_slice->top = top;
  699.       cur_slice->bottom = bottom;
  700.       cur_slice->wall = wall;
  701.       cur_slice->z = FROM_FIX_2_30(z);
  702.       cur_slice->is_back_view = active->is_back_view;
  703.  
  704.       if (cur_slice->is_back_view) {
  705.            if (bottom < cur_slice->pstart1)
  706.             bottom = cur_slice->pstart1;
  707.            if (top > cur_slice->pend2)
  708.             top = cur_slice->pend2;
  709.       } else {
  710.            if (bottom < cur_slice->pend1)
  711.             bottom = cur_slice->pend1;
  712.            if (top > cur_slice->pstart2)
  713.             top = cur_slice->pstart2;
  714.       }
  715.  
  716.       active++;
  717.       cur_slice++;
  718.      }
  719.  
  720.      /* place the 'end of column' sentinel */
  721.      cur_slice->wall = NULL;
  722.      cur_slice->top = top;
  723.      cur_slice->bottom = bottom;
  724.  
  725.      cur_slice++;
  726.  
  727.      return cur_slice;
  728. }
  729.  
  730.  
  731. static void draw_walls(Wall_intersection *int_list, View *v)
  732. {
  733.      int i = 0;
  734.      int fb_column = VIEW_WIDTH - 1;
  735.      unsigned int tex_column, tex_mask;
  736.      unsigned char *tex_base;
  737.      fixed tex_y, tex_dy;
  738.      Pixel *fb_byte, *last_byte;
  739.      fixed pstart1, pend1, pstart2, pend2;
  740.      fixed start1, start2;
  741.      fixed Vx, Vy, dVy;
  742.      fixed height = v->height;
  743.  
  744.  
  745.      Vx = v->eye_distance;
  746.      Vy = -v->view_plane_size;
  747.      dVy = fixdiv(FIXED_DOUBLE(v->view_plane_size), INT_TO_FIXED(VIEW_WIDTH));
  748.  
  749.      while (i < VIEW_WIDTH) {
  750.       Wall *wall = int_list->wall;
  751.       Texture *texture;
  752.       fixed z, t;
  753.       Boolean do_floor, do_ceiling;
  754.  
  755.       if (wall == NULL) {
  756.            int_list++;
  757.            i++;
  758.            fb_column--;
  759.            Vy += dVy;
  760.            continue;
  761.       }
  762.  
  763.       if (int_list->is_back_view) {
  764.            start1 = int_list->back->floor;
  765.            start2 = int_list->front->ceiling;
  766.            pstart1 = int_list->pend1;
  767.            pend1 = int_list->pstart1;
  768.            pstart2 = int_list->pend2;
  769.            pend2 = int_list->pstart2;
  770.       } else {
  771.            start1 = int_list->front->floor;
  772.            start2 = int_list->back->ceiling;
  773.            pstart1 = int_list->pstart1;
  774.            pend1 = int_list->pend1;
  775.            pstart2 = int_list->pstart2;
  776.            pend2 = int_list->pend2;
  777.       }      
  778.       if (pend1 > pend2)
  779.            pend1 = pend2;
  780.       if (pstart2 < pstart1)
  781.            pstart2 = pstart1;
  782.  
  783.  
  784.       texture = wall->surface_texture;
  785.       z = int_list->z;
  786.       do_floor = (pstart1 < int_list->top) &&
  787.                  (pend1 > int_list->bottom) &&
  788.              (pend1 - int_list->pstart1 > FIXED_EPSILON);
  789.       do_ceiling = (pstart2 < int_list->top) &&
  790.                    (pend2 > int_list->bottom) &&
  791.                (pend2 - int_list->pstart2 > FIXED_EPSILON);
  792.       
  793.       /* Don't do anything more with this wall if there's nothing
  794.       **   to draw.
  795.       */
  796.       if (!do_floor && !do_ceiling) {
  797.            int_list++;
  798.            continue;
  799.       }
  800.  
  801.       if (wall->sky) {
  802.            fixed angle;
  803.  
  804.            /* Compute the angle of this column.  We'll use the angle
  805.            **   exclusively to determine which texture column to
  806.            **   display for this slice of sky--that's the way sky
  807.            **   works.
  808.            */
  809.            angle = v->angle +
  810.             fixdiv(FIXED_SCALE(v->arc, i - (VIEW_WIDTH >> 1)),
  811.                INT_TO_FIXED(VIEW_WIDTH));
  812.            angle -= FIXED_SCALE(FIXED_2PI, 
  813.                     FIXED_TO_INT(fixdiv(angle, FIXED_2PI)));
  814.            angle = fixdiv(angle, FIXED_2PI);
  815.            if (angle < FIXED_ZERO)
  816.             angle = FIXED_ONE - angle;
  817.            tex_column = FIXED_TO_INT(fixmul(angle, wall->xscale)) &
  818.             (texture->width - 1);
  819.            tex_dy = fixdiv(wall->yscale, INT_TO_FIXED(VIEW_HEIGHT));
  820.       } else {
  821.            t = wall_ray_intersection(Vx, Vy, wall);
  822.            /* From t, calculate the integer coordinates in the texture
  823.         **   bitmap.  For efficiency, we assume that the width of the
  824.         **   texture is a power of two.
  825.         */
  826.            tex_column =
  827.             FIXED_TO_INT(wall->xphase + fixmul(t, wall->xscale)) &
  828.              (texture->width - 1);
  829.            /* Test to avoid overflow here . . .  if the wall is so far
  830.         **   away that z (which is 1 / distance) is less than
  831.         **   FIXED_EPSILON, then it will be so small when rendered
  832.         **   that we can use a bogus value for tex_dy.
  833.         */
  834.            if (z < FIXED_EPSILON)
  835.             tex_dy = 0;
  836.            else
  837.             tex_dy = fixdiv(wall->yscale, FIXED_SCALE(z, VIEW_HEIGHT));
  838.       }
  839.       tex_mask = texture->height - 1;
  840.       tex_base = texture->texels + (tex_column << texture->log2width);
  841.  
  842.       /* Now we can actually draw the walls, starting with
  843.       **   the floor segment.
  844.       */
  845.       if (do_floor) {
  846.            int fb_start, fb_end;
  847.  
  848.            if (pstart1 < int_list->bottom) {
  849.             pstart1 = int_list->bottom;
  850.             start1 = fixdiv(pstart1, z) + height;
  851.            }
  852.            if (pend1 > int_list->top)
  853.             pend1 = int_list->top;
  854.  
  855.            fb_start = FIXED_TO_INT(FIXED_SCALE(pstart1, VIEW_HEIGHT));
  856.            fb_end = FIXED_TO_INT(FIXED_SCALE(pend1, VIEW_HEIGHT));
  857.  
  858.            fb_start = (VIEW_HEIGHT >> 1) - fb_start;
  859.            /* The following line of code is a hack . . . */
  860.            if (fb_start >= VIEW_HEIGHT)
  861.             fb_start = VIEW_HEIGHT - 1;
  862.            fb_end = (VIEW_HEIGHT >> 1) - fb_end;
  863.            fb_byte = fb->pixels + fb_column + fb_rows[fb_start];
  864.            last_byte = fb->pixels + fb_column + fb_rows[fb_end];
  865.            if (wall->sky)
  866.             tex_y = fixmul(view_constants.row_view[fb_start],
  867.                    wall->yscale);
  868.            else
  869.             tex_y = fixmul(start1, wall->yscale) + wall->yphase;
  870.  
  871.            draw_wall_slice(fb_byte, last_byte, tex_base,
  872.                    tex_y, tex_dy, VIEW_WIDTH, texture->height,
  873.                    fb_start - fb_end);
  874.       }
  875.  
  876.       /* Draw the ceiling segment. */
  877.       if (do_ceiling) {
  878.            int fb_start, fb_end;
  879.  
  880.            if (pstart2 < int_list->bottom) {
  881.             pstart2 = int_list->bottom;
  882.             start2 = fixdiv(pstart2, z) + height;
  883.            }
  884.            if (pend2 > int_list->top)
  885.             pend2 = int_list->top;
  886.  
  887.            fb_start = FIXED_TO_INT(FIXED_SCALE(pstart2, VIEW_HEIGHT));
  888.            fb_end = FIXED_TO_INT(FIXED_SCALE(pend2, VIEW_HEIGHT));
  889.  
  890.            fb_start = (VIEW_HEIGHT >> 1) - fb_start;
  891.            /* The following line of code is a hack . . . */
  892.            if (fb_start >= VIEW_HEIGHT)
  893.             fb_start = VIEW_HEIGHT - 1;
  894.            fb_end = (VIEW_HEIGHT >> 1) - fb_end;
  895.            fb_byte = fb->pixels + fb_column + fb_rows[fb_start];
  896.            last_byte = fb->pixels + fb_column + fb_rows[fb_end];
  897.            if (wall->sky)
  898.             tex_y = view_constants.row_view[fb_start];
  899.            else
  900.             tex_y = fixmul(start2, wall->yscale) + wall->yphase;
  901.  
  902.            draw_wall_slice(fb_byte, last_byte, tex_base,
  903.                    tex_y, tex_dy, VIEW_WIDTH, texture->height,
  904.                    fb_start - fb_end);
  905.       }
  906.  
  907.       int_list++;
  908.      }
  909. }
  910.  
  911.  
  912. static void draw_floors(Wall_intersection *int_list, View *v)
  913. {
  914.      int i;
  915.      Wall_intersection *last, *current;
  916.      Wall_intersection *save_current, *save_last;
  917.      fixed start, end;
  918.      fixed max_floor, min_ceiling;
  919.      fixed last_max_floor = FIXED_ZERO, last_min_ceiling = FIXED_ZERO;
  920.  
  921.  
  922.      last = current = int_list;
  923.  
  924.      for (i = 1; i < VIEW_WIDTH; i++) {
  925.  
  926.       for (; current->wall != NULL; current++) ;
  927.       save_current = ++current;
  928.       save_last = last;
  929.  
  930.       /* get the highest floor and lowest ceiling */
  931.       while (current->wall != NULL)
  932.            current++;
  933.       max_floor = current->bottom;
  934.       min_ceiling = current->top;
  935.  
  936.       current = save_current;
  937.             
  938.       /* draw floors */
  939.       while (last->wall != NULL && current->wall != NULL) {
  940.  
  941.            if (current->front == last->front)
  942.             start = MAX(last->bottom, current->pstart1);
  943.            else
  944.             start = MAX(last->bottom, current->bottom);
  945.            end = MIN(last->pstart1, current->pend1);
  946.  
  947.            if (start < end)
  948.             draw_floor_slices(last->front, start, end, v, i - 1);
  949.  
  950.            if (last->pend1 < current->pend1)
  951.             last++;
  952.            else
  953.             current++;
  954.       }
  955. /*
  956.       while (last->wall != NULL) {
  957.            end = last->bottom;
  958.            start = MAX(min_ceiling, last->pstart1);
  959.            if (start < end)
  960.             draw_floor_slices(last->front, start, end, v, i - 1);
  961.            last++;
  962.       }
  963. */
  964.       /* draw ceiling slices generated by ceiling walls */
  965.       last = save_last;
  966.       current = save_current;
  967.       while (last->wall != NULL && current->wall != NULL) {
  968.  
  969.            if (current->front == last->front)
  970.             end = MIN(last->top, current->pend2);
  971.            else
  972.             end = MIN(last->top, current->top);
  973.            start = MAX(last->pend2, current->pstart2);
  974.  
  975.            if (start < end)
  976.             draw_ceiling_slices(last->front, start, end, v, i - 1);
  977.  
  978.            if (last->pstart2 > current->pstart2)
  979.             last++;
  980.            else
  981.             current++;
  982.       }
  983.  
  984.       /* draw ceiling slices generated by floor walls */
  985.       while (last->wall != NULL) {
  986.            start = MAX(last_max_floor, last->pend2);
  987.            end = MIN(max_floor, last->top);
  988.            if (start < end)
  989.             draw_ceiling_slices(last->front, start, end, v, i - 1);
  990.            last++;
  991.       }
  992.       last = save_current;
  993.  
  994.       last_max_floor = max_floor;
  995.       last_min_ceiling = min_ceiling;
  996.      }
  997.  
  998.      save_last = last;
  999.  
  1000.      /* finish up the floors */
  1001.      while (last->wall != NULL) {
  1002.  
  1003.       start = MAX(last->bottom, -FIXED_ONE_HALF);
  1004.       end = MIN(last->pstart1, FIXED_ZERO);
  1005.  
  1006.       if (start < end)
  1007.            draw_floor_slices(last->front, start, end, v, i - 1);
  1008.  
  1009.       last++;
  1010.      }
  1011.  
  1012.      /* finish up the ceilings */
  1013.      last = save_last;
  1014.      while (last->wall != NULL) {
  1015.  
  1016.       start = MAX(last->pend2, FIXED_ZERO);
  1017.       end = MIN(last->top, FIXED_ONE_HALF);
  1018.  
  1019.       if (start < end)
  1020.            draw_ceiling_slices(last->front, start, end, v, i - 1);
  1021.  
  1022.       last++;
  1023.      }
  1024.  
  1025. }
  1026.  
  1027.  
  1028. static void draw_floor_slices(Region *r, fixed start, fixed end,
  1029.                   View *v, int column)
  1030. {
  1031.      int screen_column = VIEW_WIDTH - column - 1;
  1032.      int start_row, end_row;
  1033.      Texture *texture = r->floor_tex;
  1034.      fixed height;
  1035.      fixed sin_x, cos_x;
  1036.  
  1037.  
  1038.      start_row = FIXED_TO_INT(start * VIEW_HEIGHT);
  1039.      end_row = FIXED_TO_INT(end * VIEW_HEIGHT);
  1040.      /* Bail out early and save time if there's nothing to draw. */
  1041.      if (start_row == end_row)
  1042.       return;
  1043.  
  1044.      start_row = (VIEW_HEIGHT >> 1) - start_row;
  1045.      end_row = (VIEW_HEIGHT >> 1) - end_row;
  1046.      if (start_row >= VIEW_HEIGHT)
  1047.       start_row = VIEW_HEIGHT - 1;
  1048.      if (end_row < 0)
  1049.       end_row = 0;
  1050.  
  1051.      height = r->floor - v->height;
  1052.      sin_x = view_constants.sin_tab[column];
  1053.      cos_x = view_constants.cos_tab[column];
  1054.  
  1055.  
  1056.      while (start_row >= end_row) {
  1057.       fixed x, dx, y, dy;
  1058.       fixed y1;
  1059.       Pixel *fb_byte = fb->pixels + fb_rows[start_row] + screen_column;
  1060.  
  1061.       if (FIXED_ABS(view_constants.row_view[start_row]) < FIXED_EPSILON)
  1062.            y1 = FIXED_ONE;
  1063.       else
  1064.            y1 = fixdiv(height, view_constants.row_view[start_row]) << 4;
  1065.  
  1066.       x = fixmul(view_constants.view_sin - cos_x, y1) - (v->y << 4);
  1067.       y = fixmul(-view_constants.view_cos - sin_x, y1) - (v->x << 4);
  1068.       dx = fixmul(view_constants.cos_dx, y1);
  1069.       dy = fixmul(view_constants.sin_dx, y1);
  1070.  
  1071.       draw_floor_slice(fb_byte, texture->texels, x, y, dx, dy, 
  1072.                texture->width);
  1073.  
  1074.       start_row--;
  1075.      }
  1076. }
  1077.  
  1078.  
  1079. static void draw_ceiling_slices(Region *r, fixed start, fixed end,
  1080.                 View *v, int column)
  1081. {
  1082.      int screen_column = VIEW_WIDTH - column - 1;
  1083.      int start_row, end_row;
  1084.      Texture *texture = r->ceiling_tex;
  1085.      fixed height;
  1086.      fixed sin_x, cos_x;
  1087.  
  1088.  
  1089.      start_row = FIXED_TO_INT(start * VIEW_HEIGHT);
  1090.      end_row = FIXED_TO_INT(end * VIEW_HEIGHT);
  1091.      /* Bail out early and save time if there's nothing to draw. */
  1092.      if (start_row == end_row)
  1093.       return;
  1094.  
  1095.      start_row = (VIEW_HEIGHT >> 1) - start_row;
  1096.      end_row = (VIEW_HEIGHT >> 1) - end_row;
  1097.      if (start_row >= VIEW_HEIGHT)
  1098.       start_row = VIEW_HEIGHT - 1;
  1099.      if (end_row < 0)
  1100.       end_row = 0;
  1101.  
  1102.      height = r->ceiling - v->height;
  1103.      sin_x = view_constants.sin_tab[column];
  1104.      cos_x = view_constants.cos_tab[column];
  1105.  
  1106.  
  1107.      while (start_row >= end_row) {
  1108.       fixed x, dx, y, dy;
  1109.       fixed y1;
  1110.       Pixel *fb_byte = fb->pixels + fb_rows[start_row] + screen_column;
  1111.  
  1112.       if (FIXED_ABS(view_constants.row_view[start_row]) < FIXED_EPSILON)
  1113.            y1 = FIXED_ONE;
  1114.       else
  1115.            y1 = fixdiv(height, view_constants.row_view[start_row]) << 4;
  1116.  
  1117.       x = fixmul(view_constants.view_sin - cos_x, y1) - (v->y << 4);
  1118.       y = fixmul(-view_constants.view_cos - sin_x, y1) - (v->x << 4);
  1119.       dx = fixmul(view_constants.cos_dx, y1);
  1120.       dy = fixmul(view_constants.sin_dx, y1);
  1121.  
  1122.       draw_floor_slice(fb_byte, texture->texels, x, y, dx, dy, 
  1123.                texture->width);
  1124.  
  1125.       start_row--;
  1126.      }
  1127. }
  1128.  
  1129.  
  1130. /* Calculate the value of the parameter t at the intersection
  1131. **   of the view ray and the wall.  t is 0 at the origin of
  1132. **   the wall, and 1 at the other endpoint.
  1133. */
  1134. static fixed wall_ray_intersection(fixed Vx, fixed Vy, Wall *wall)
  1135. {
  1136.      fixed denominator, Nx, Ny, Wx, Wy;
  1137.      
  1138.      Nx = -Vy;
  1139.      Ny = Vx;
  1140.      Wx = wall->vertex2->tx - wall->vertex1->tx;
  1141.      Wy = wall->vertex2->ty - wall->vertex1->ty;
  1142.  
  1143.      denominator = fixmul(Nx, Wx) + fixmul(Ny, Wy); /* N dot W */
  1144.      if (denominator < FIXED_EPSILON)
  1145.       return FIXED_ONE - fixdiv(fixmul(Nx, wall->vertex1->tx) +
  1146.                     fixmul(Ny, wall->vertex1->ty),
  1147.                     -denominator);
  1148.      else if (denominator > FIXED_EPSILON)
  1149.       return fixdiv(fixmul(Nx, wall->vertex1->tx) +
  1150.             fixmul(Ny, wall->vertex1->ty),
  1151.             -denominator);
  1152.      else
  1153.       return FIXED_ZERO;
  1154. }
  1155.  
  1156.  
  1157. static void init_buffers(void)
  1158. {
  1159.      int i;
  1160.  
  1161.      for (i = 0; i < VIEW_WIDTH + 1; i++) {
  1162.       start_events[i].n_events = 0;
  1163.       end_events[i].n_events = 0;
  1164.      }
  1165. }
  1166.  
  1167.  
  1168. /* Calculate values that are dependent only on the screen dimensions and
  1169. **   the view.
  1170. */
  1171. static void calc_view_constants(View *v, int screen_width, int screen_height)
  1172. {
  1173.      static int last_height = 0, last_width = 0;
  1174.      int i;
  1175.      fixed x, y;
  1176.  
  1177.      
  1178.      /* Make sure that enough memory has been allocated for the tables. */
  1179.      if (screen_height != last_height) {
  1180.       if (last_height == 0)
  1181.            view_constants.row_view =
  1182.             wtmalloc(screen_height * sizeof(fixed));
  1183.       else
  1184.            view_constants.row_view =
  1185.             wtrealloc(view_constants.row_view,
  1186.                  screen_height * sizeof(fixed));
  1187.       last_height = screen_height;
  1188.      }
  1189.      if (screen_width != last_width) {
  1190.       if (last_width == 0) {
  1191.            view_constants.sin_tab = wtmalloc(screen_width * sizeof(fixed));
  1192.            view_constants.cos_tab = wtmalloc(screen_width * sizeof(fixed));
  1193.       } else {
  1194.            view_constants.sin_tab =
  1195.             wtrealloc(view_constants.sin_tab,
  1196.                   screen_width * sizeof(fixed));
  1197.            view_constants.cos_tab = 
  1198.             wtrealloc(view_constants.cos_tab,
  1199.                   screen_width * sizeof(fixed));
  1200.       }
  1201.       last_width = screen_width;
  1202.      }
  1203.       
  1204.      view_constants.view_sin =
  1205.       FLOAT_TO_FIXED(sin(- FIXED_TO_FLOAT(v->angle)));
  1206.      view_constants.view_cos =
  1207.       FLOAT_TO_FIXED(cos(- FIXED_TO_FLOAT(v->angle)));
  1208.      view_constants.screen_dx = fixdiv(FIXED_DOUBLE(v->view_plane_size),
  1209.                        INT_TO_FIXED(screen_width));
  1210.      view_constants.screen_dy = fixdiv(FIXED_ONE,
  1211.                        INT_TO_FIXED(screen_height));
  1212.      view_constants.sin_dx = fixmul(view_constants.view_sin,
  1213.                     view_constants.screen_dx);
  1214.      view_constants.cos_dx = fixmul(view_constants.view_cos,
  1215.                     view_constants.screen_dx);
  1216.      y = FIXED_SCALE(view_constants.sin_dx, -(screen_width >> 1));
  1217.      x = FIXED_SCALE(view_constants.cos_dx, -(screen_width >> 1));
  1218.      for (i = 0; i < screen_width; i++) {
  1219.       view_constants.sin_tab[i] = y;
  1220.       view_constants.cos_tab[i] = x;
  1221.       y += view_constants.sin_dx;
  1222.       x += view_constants.cos_dx;
  1223.      }
  1224.      
  1225.      y = FIXED_SCALE(view_constants.screen_dy, screen_height >> 1);
  1226.      for (i = 0; i < screen_height; i++) {
  1227.       view_constants.row_view[i] = y;
  1228.       y -= view_constants.screen_dy;
  1229.      }
  1230. }
  1231.